﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WorkFlowCore.Conditions;
using WorkFlowCore.EventData;
using WorkFlowCore.IRepositories;
using WorkFlowCore.UserSelectors;
using WorkFlowCore.Workflows;
using WorkFlowCore.WorkTasks;
using Volo.Abp.Uow;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Domain.Services;
using WorkFlowCore.Authorization;

namespace WorkFlowCore.Workflows
{
    public class WorkflowManager : IDomainService
    {
        private readonly IWorkflowRepository workflowRepository;
        private readonly IBasicRepository<WorkflowVersionInfo, Guid> versionRepository;
        private readonly IWorkTaskRepository workTaskRepository;
        private readonly IWorkStepRepository workStepRepository;
        private readonly ConditionManager conditionManager;
        private readonly UserSelectorManager userSelectorManager;
        private readonly ILocalEventBus eventManager;
        private readonly IUnitOfWorkManager unitOfWorkManager;
        private readonly WorkflowStore workflowStore;
        private readonly IWorkflowSession workflowSession;

        public WorkflowManager(IWorkflowRepository workflowRepository, IBasicRepository<WorkflowVersionInfo, Guid> versionRepository, IWorkTaskRepository workTaskRepository, IWorkStepRepository workStepRepository, ConditionManager conditionManager, UserSelectorManager userSelectorManager, ILocalEventBus eventManager, IUnitOfWorkManager unitOfWorkManager, WorkflowStore workflowStore, IWorkflowSession workflowSession)
        {
            this.workflowRepository = workflowRepository;
            this.versionRepository = versionRepository;
            this.workTaskRepository = workTaskRepository;
            this.workStepRepository = workStepRepository;
            this.conditionManager = conditionManager;
            this.userSelectorManager = userSelectorManager;
            this.eventManager = eventManager;
            this.unitOfWorkManager = unitOfWorkManager;
            this.workflowStore = workflowStore;
            this.workflowSession = workflowSession;
        }


        #region 流程的增删改查

        public async Task<List<Workflow>> GetAllWorkflowsWithVersion()
        {
            var result = await workflowRepository.GetAllWorkflowsWithVersion();
            return result;
        }

        /// <summary>
        /// 根据编号获取流程设计
        /// </summary>
        /// <param name="workflowNo"></param>
        /// <returns></returns>
        public async Task<Workflow> GetWorkflowByNo(string workflowNo)
        {
            return await workflowRepository.GetAsync(w => w.WorkflowNo == workflowNo);
        }
        /// <summary>
        /// 流程设计创建
        /// </summary>
        /// <param name="name"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        public async Task<Workflow> CreateWorkflow(string name, string description, string workflowType)
        {
            return await CreateWorkflow($"WF{DateTime.Now.ToString("yyyMMddHHmmssfff")}", name, description, workflowType);
        }

        /// <summary>
        /// 流程设计创建
        /// </summary>
        /// <param name="name"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        public async Task<Workflow> CreateWorkflow(string wfno, string name, string description, string workflowType)
        {
            var count = await workflowRepository.GetCountAsync(w => w.Name == name);
            if (count > 0) throw new Exception("同名流程设计已存在，考虑在现有流程基础增加版本");

            var workflowNo = wfno ?? $"WF{DateTime.Now.ToString("yyyMMddHHmmssfff")}";
            var workflow = new Workflow(Guid.NewGuid(), workflowNo, name, description, workflowType);
            workflow = await workflowRepository.InsertAsync(workflow);
            var workflowVersion = new WorkflowVersion(Guid.NewGuid(), workflow.Id, workflow.ActiveVersion, string.Empty, description);
            await versionRepository.InsertAsync(workflowVersion.ToWorkflowVersionInfo());
            return workflow;


        }
        /// <summary>
        /// 更新流程激活版本
        /// </summary>
        /// <param name="workflowId"></param>
        /// <param name="activeVersion"></param>
        public async Task<bool> UpdateWorkflowActiveVersion(Guid workflowId, int activeVersion)
        {
            using (var unitOfWork = unitOfWorkManager.Begin())
            {
                var workflow = await workflowRepository.GetAsync(workflowId);
                workflow.UpdateActiveVersion(activeVersion);
                await workflowRepository.UpdateAsync(workflow);
                return unitOfWork.Commit(true, false);
            }
        }
        /// <summary>
        /// 删除流程
        /// </summary>
        /// <param name="workflowid"></param>
        /// <returns></returns>
        public async Task<bool> DeleteWorkflow(Guid workflowid)
        {
            //有被流程引用则不允许删除
            if ((await workTaskRepository.GetCountAsync(wt => wt.WorkflowId_Id == workflowid)) > 0)
            {
                throw new Exception("流程使用中，无法删除");
            }

            var workflows = await workflowRepository.GetListAsync(wt => wt.Id == workflowid);
            if (workflows.Any(w => w.IsLock))
            {
                throw new Exception("已锁定无法编辑");
            }
            workflows.ForEach(t => t.Delete(workflowSession.User?.Id));

            using (var unitOfWork = unitOfWorkManager.Begin())
            {
                await workflowRepository.UpdateManyAsync(workflows);
                if (!unitOfWork.Commit(true, false))
                {
                    throw new Exception("删除失败");
                }
                return true;
            }
        }
        /// <summary>
        /// 删除流程版本
        /// </summary>
        /// <param name="workflowid"></param>
        /// <param name="versionNo"></param>
        /// <returns></returns>
        public async Task<bool> DeleteWorkflowVersion(Guid workflowid, int versionNo)
        {

            //有被流程引用则不允许删除
            if ((await workTaskRepository.GetCountAsync(wt => wt.WorkflowId_Id == workflowid && wt.WorkflowId_VersionNo == versionNo)) > 0)
            {
                throw new Exception("流程使用中，无法删除");
            }
            using (var unitOfWork = unitOfWorkManager.Begin())
            {
                await versionRepository.DeleteManyAsync(wt => wt.WorkflowId == workflowid && wt.VersionNo == versionNo && wt.IsLock == false);
                return unitOfWork.Commit(true, false);
            }
        }

        /// <summary>
        /// 节点映射
        /// </summary>
        /// <param name="workflowLines"></param>
        /// <param name="workflowNodes"></param>
        /// <returns></returns>
        private List<NodeMap> GetNewNodeMaps(List<WorkflowLine> workflowLines, List<WorkflowNode> workflowNodes)
        {
            var result = new List<NodeMap>();
            workflowLines.ForEach(line =>
            {
                var fromNode = workflowNodes.FirstOrDefault(n => n.Id == line.FromNodeId);
                var toNode = workflowNodes.FirstOrDefault(n => n.Id == line.ToNodeId);

                result.Add(NodeMap.Factory.NewNormalNodeMap(fromNode, toNode, line.Conditions));

                //如果指定拒绝节点，则按照拒绝节点配置拒绝映射，否则原路拒绝
                if (toNode.RejectNodes != null && toNode.RejectNodes.Any())
                {
                    toNode.RejectNodes.ForEach(rn =>
                    {
                        var rejectFromNode = toNode;
                        var rejectToNode = workflowNodes.FirstOrDefault(n => n.Id == rn.NodeId);

                        result.Add(NodeMap.Factory.NewRejectNodeMap(rejectFromNode, rejectToNode, rn.Conditions));
                    });
                }
                else
                {
                    //判断起始节点是否是开始或会签节点，是的话，直接退回，不是的话，获取来所有以起始节点为终点的所有节点条件放入拒绝判断列表中
                    if (fromNode.NodeType == WorkNodeType.Begin || fromNode.NodeType == WorkNodeType.Sign || fromNode.NodeType == WorkNodeType.Judge || fromNode.NodeType == WorkNodeType.HandOut)
                    {
                        result.Add(NodeMap.Factory.NewRejectNodeMap(toNode, fromNode, null));
                    }
                    else
                    {
                        var fromLines = workflowLines.Where(line => line.ToNodeId == fromNode.Id).ToList();
                        fromLines.ForEach(line =>
                        {
                            result.Add(NodeMap.Factory.NewRejectNodeMap(toNode, fromNode, line.Conditions));
                        });
                    }
                }
            });
            return result;
        }

        /// <summary>
        /// 连接子流程链路
        /// </summary>
        /// <param name="nodeMaps"></param>
        private void ConnectSubProcessNodes(List<NodeMap> nodeMaps)
        {
            //先清空历史链路
            nodeMaps.ForEach(nm =>
            {
                nm.FromNode.SubProcessNodeId = null;
                nm.ToNode.SubProcessNodeId = null;
            });

            //递归连接链路
            //先选出子流程节点，然后再顺藤摸瓜指定下去
            var subProcessNodes = nodeMaps.Where(nm => nm.FromNode.NodeType == WorkNodeType.SubProcess).Select(nm => nm.FromNode);
            foreach (var subProcessNode in subProcessNodes)
            {
                ConnectSubProcessNodes(nodeMaps, subProcessNode, subProcessNode);
            }

            //TODO 检查每个子流程是否都有完整的回路
        }
        /// <summary>
        /// 递归连接节点
        /// </summary>
        /// <param name="nodeMaps"></param>
        /// <param name="subProcessNode"></param>
        /// <param name="fromNode"></param>
        private void ConnectSubProcessNodes(List<NodeMap> nodeMaps, WorkflowNode subProcessNode, WorkflowNode fromNode)
        {
            var toNodes = nodeMaps.Where(nm => nm.FromNode == fromNode && nm.ToNode.NodeType == WorkNodeType.SubNode).Select(nm => nm.ToNode);
            foreach (var node in toNodes)
            {
                //如果目标卡跟子流程节点一样，就停止了
                if (node == subProcessNode) continue;
                //遇到桥套子流程，则先递归遍历子流程
                if (node.NodeType == WorkNodeType.SubProcess)
                    ConnectSubProcessNodes(nodeMaps, node, node);

                node.SubProcessNodeId = subProcessNode.Id;
                ConnectSubProcessNodes(nodeMaps, subProcessNode, node);
            }
        }

        private async Task<WorkflowVersionInfo> GetWorkflowVersionInfo(Guid workflowId, int versionNo)
        {
            return await workflowStore.GetWorkflowVersionInfo(workflowId, versionNo);
        }
        public async Task<WorkflowVersion> GetWorkflowVersion(Guid workflowId, int versionNo)
        {
            return await workflowStore.GetWorkflowVersion(workflowId, versionNo);
        }

        /// <summary>
        /// 更新流程设计
        /// </summary>
        /// <param name="workflowId"></param>
        /// <param name="name"></param>
        /// <param name="description"></param>
        /// <param name="versionNo"></param>
        /// <param name="drawingInfo"></param>
        /// <param name="versionDescription"></param>
        /// <param name="workflowLines"></param>
        /// <param name="workflowNodes"></param>
        /// <returns></returns>

        public async Task<Workflow> UpdateWorkflow(Guid workflowId, string name, string description, string workflowType, int versionNo, string drawingInfo, string versionDescription, List<WorkflowLine> workflowLines, List<WorkflowNode> workflowNodes)
        {
            var workflow = await workflowRepository.GetAsync(workflowId);
            return await UpdateWorkflow(workflow, name, description, workflowType, versionNo, drawingInfo, versionDescription, workflowLines, workflowNodes);
        }


        /// <summary>
        /// 更新流程设计
        /// </summary>
        /// <param name="workflow"></param>
        /// <param name="name"></param>
        /// <param name="description"></param>
        /// <param name="versionNo"></param>
        /// <param name="drawingInfo"></param>
        /// <param name="versionDescription"></param>
        /// <param name="workflowLines"></param>
        /// <param name="workflowNodes"></param>
        /// <returns></returns>

        public async Task<Workflow> UpdateWorkflow(Workflow workflow, string name, string description, string workflowType, int versionNo, string drawingInfo, string versionDescription, List<WorkflowLine> workflowLines, List<WorkflowNode> workflowNodes)
        {
            var workflowId = workflow.Id;
            if (workflowNodes.Count(n => n.NodeType == WorkNodeType.Begin) != 1)
                throw new Exception("至少也只能有一个开始节点");
            if (workflowNodes.Count(n => n.NodeType == WorkNodeType.End) != 1)
                throw new Exception("至少也只能有一个结束节点");

            if (workflowLines.Count == 0)
                throw new Exception("缺少节点间关系连线");


            if (workflowLines.Any(line => line.FromNodeId == null || string.IsNullOrEmpty(line.FromNodeId.ToString()) || line.ToNodeId == null || string.IsNullOrEmpty(line.ToNodeId.ToString())))
                throw new Exception("连线必须有起点和终点");

            var startmNode = workflowNodes.FirstOrDefault(n => n.NodeType == WorkNodeType.Begin);
            if (!workflowLines.Any(line => line.FromNodeId == startmNode.Id) || workflowLines.Any(line => line.FromNodeId == startmNode.Id && (line.ToNodeId == null || line.ToNodeId.Equals(Guid.Empty))))
                throw new Exception("起始节点必须连线目标节点的连线");

            var endNode = workflowNodes.FirstOrDefault(n => n.NodeType == WorkNodeType.End);
            if (!workflowLines.Any(line => line.ToNodeId == endNode.Id) || workflowLines.Any(line => line.ToNodeId == endNode.Id && (line.FromNodeId == null || line.FromNodeId.Equals(Guid.Empty))))
                throw new Exception("结束节点必须有来源节点的连线");

            if (workflowNodes.Where(n => n.NodeType != WorkNodeType.Begin && n.NodeType != WorkNodeType.End).Any(node => !workflowLines.Any(line => line.FromNodeId == node.Id) || !workflowLines.Any(line => line.ToNodeId == node.Id)))
                throw new Exception("非起止节点至少有来源节点和目标节点的连线");

            var count = await workflowRepository.GetCountAsync(w => w.Name == name && w.Id != workflowId);
            if (count > 0) throw new Exception("同名流程设计已存在，考虑在现有流程基础增加版本");


            var toNode = workflowNodes.FirstOrDefault(n => n.NodeType == WorkNodeType.Begin);

            if (workflow.IsLock)
            {
                throw new Exception("已锁定无法编辑");
            }

            workflow.Update(name, description, workflowType);
            var existesWorkflow =(await workflowRepository.GetCountAsync(wf=>wf.Id== workflowId)) >0;
            _ = existesWorkflow?await workflowRepository.UpdateAsync(workflow):await workflowRepository.InsertAsync(workflow);
            WorkflowVersion version = null;
            var versionInfo = await GetWorkflowVersionInfo(workflowId, versionNo);

            if (versionInfo == null)
            {
                //新建版本
                version = new WorkflowVersion(Guid.NewGuid(), workflowId, versionNo, drawingInfo, versionDescription);
                var nodeMaps = GetNewNodeMaps(workflowLines, workflowNodes);
                ConnectSubProcessNodes(nodeMaps);
                version.SetNodeMaps(nodeMaps);
                await versionRepository.InsertAsync(version.ToWorkflowVersionInfo());
            }
            else
            {
                version = versionInfo.ToWorkflowVersion();

                if (version.IsLock)
                {
                    throw new Exception("已锁定无法编辑");
                }

                var nodeMaps = GetNewNodeMaps(workflowLines, workflowNodes);
                ConnectSubProcessNodes(nodeMaps);
                version.SetNodeMaps(nodeMaps);
                version.Update(versionNo, drawingInfo, versionDescription);
                await versionRepository.UpdateAsync(version.ToWorkflowVersionInfo(versionInfo));
            }

            return unitOfWorkManager.Current.Commit(workflow, null);
        }

        #endregion

        public async Task ClearEmptyWorkflows()
        {
            //自动清理一天前的
            var beforeTime = DateTime.Now.AddDays(-1);
            var workflowVersions = await versionRepository.GetListAsync(v => v.Deleted == false && (v.DrawingInfo == "" || v.DrawingInfo == null) && v.CreationTime < beforeTime);

            foreach (var workflowVersion in workflowVersions)
            {
                await versionRepository.DeleteAsync(workflowVersion.Id);
                if ((await versionRepository.GetCountAsync(w => !w.Deleted && w.WorkflowId == workflowVersion.WorkflowId && w.Id != workflowVersion.Id)) == 0)
                {
                    await workflowRepository.DeleteAsync(workflowVersion.WorkflowId);
                }
                var worktasks = await workTaskRepository.GetListAsync(wt => wt.WorkflowId_Id == workflowVersion.WorkflowId && wt.WorkflowId_VersionNo == workflowVersion.VersionNo);
                var worktaskIds = worktasks.Select(wt => wt.Id).ToList();
                await workTaskRepository.DeleteManyAsync(worktaskIds);
            }

        }
        /// <summary>
        /// 创建或者更新流程设计
        /// </summary>
        /// <param name="workflowId"></param>
        /// <param name="name"></param>
        /// <param name="description"></param>
        /// <param name="workflowType"></param>
        /// <param name="versionNo"></param>
        /// <param name="drawingInfo"></param>
        /// <param name="versionDescription"></param>
        /// <param name="workflowLines"></param>
        /// <param name="workflowNodes"></param>
        /// <returns></returns>
        public async Task<Workflow> CreateUpdateWorkflow(Guid workflowId, string name, string description, string workflowType, int versionNo, string drawingInfo, string versionDescription, List<WorkflowLine> workflowLines, List<WorkflowNode> workflowNodes)
        {
            var isCreate = false;
            Workflow workflow = null;
            if (workflowId == Guid.Empty)
            {
                isCreate = true;
            }
            else
            {
                workflow = await workflowRepository.GetAsync(workflowId);
                if (workflow == null)
                {
                    isCreate = true;
                }
            }

            if (isCreate)
            {
                var count = await workflowRepository.GetCountAsync(w => w.Name == name);
                if (count > 0) throw new Exception("同名流程设计已存在，考虑在现有流程基础增加版本");

                var workflowNo = $"WF{DateTime.Now.ToString("yyyMMddHHmmssfff")}";
                workflow = new Workflow(Guid.NewGuid(), workflowNo, name, description, workflowType);
            }
            if (workflow != null)
            {
                workflow = await UpdateWorkflow(workflow, name, description, workflowType, versionNo, drawingInfo, versionDescription, workflowLines, workflowNodes);
            }
            return workflow;
        }

        /// <summary>
        /// 锁定流程版本
        /// </summary>
        /// <param name="workflowid"></param>
        /// <param name="versionNo"></param>
        /// <returns></returns>
        public async Task<bool> LockWorkflowVersion(Guid workflowid, int versionNo)
        {
            var versionInfo = (await versionRepository.GetListAsync(wt => wt.WorkflowId == workflowid && wt.VersionNo == versionNo && wt.IsLock == false)).FirstOrDefault();
            if (versionInfo != null)
            {
                versionInfo.IsLock = true;
                await versionRepository.UpdateAsync(versionInfo);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 锁定流程
        /// </summary>
        /// <param name="workflowid"></param>
        /// <returns></returns>
        public async Task<bool> LockWorkflow(Guid workflowid)
        {
            var workflow = await workflowRepository.GetAsync(workflowid);
            if (workflow != null)
            {
                workflow.IsLock = true;
                await workflowRepository.UpdateAsync(workflow);
                return true;
            }
            return false;
        }
    }
}
